﻿using System.Collections.Concurrent;
using System.Reflection;
using HSGD.Common.Extensions;
using Microsoft.Extensions.Logging;
using SqlSugar;

namespace HSGD.Repository.UnitOfWorks {
    public class UnitOfWorkManage : IUnitOfWorkManage {
        private readonly ILogger<UnitOfWorkManage> _logger;
        private readonly ISqlSugarClient _sqlSugarClient;

        private int _tranCount { get; set; }
        public int TranCount => _tranCount;
        public readonly ConcurrentStack<string> TranStack = new();

        public UnitOfWorkManage(ISqlSugarClient sqlSugarClient, ILogger<UnitOfWorkManage> logger) {
            _sqlSugarClient = sqlSugarClient;
            _logger = logger;
            _tranCount = 0;
        }

        /// <summary>
        /// 获取DB，保证唯一性
        /// </summary>
        /// <returns></returns>
        public SqlSugarScope GetDbClient() {
            // 必须要as，后边会用到切换数据库操作
            return _sqlSugarClient as SqlSugarScope;
        }


        public UnitOfWork CreateUnitOfWork() {
            UnitOfWork uow = new UnitOfWork();
            uow.Logger = _logger;
            uow.Db = _sqlSugarClient;
            uow.Tenant = (ITenant)_sqlSugarClient;
            uow.IsTran = true;

            uow.Db.Open();
            uow.Tenant.BeginTran();
            _logger.LogDebug("UnitOfWork Begin");
            return uow;
        }

        public void BeginTran() {
            lock (this) {
                _tranCount++;
                GetDbClient().BeginTran();
            }
        }

        public void BeginTran(MethodInfo method) {
            lock (this) {
                GetDbClient().BeginTran();
                TranStack.Push(method.GetFullName());
                _tranCount = TranStack.Count;
            }
        }

        public void CommitTran() {
            lock (this) {
                _tranCount--;
                if (_tranCount == 0) {
                    try {
                        GetDbClient().CommitTran();
                    }
                    catch (Exception ex) {
                        Console.WriteLine(ex.Message);
                        GetDbClient().RollbackTran();
                    }
                }
            }
        }

        public void CommitTran(MethodInfo method) {
            lock (this) {
                string result = "";
                while (!TranStack.IsEmpty && !TranStack.TryPeek(out result)) {
                    Thread.Sleep(1);
                }


                if (result == method.GetFullName()) {
                    try {
                        GetDbClient().CommitTran();

                        _logger.LogDebug($"Commit Transaction");
                        Console.WriteLine($"Commit Transaction");
                    }
                    catch (Exception ex) {
                        Console.WriteLine(ex.Message);
                        GetDbClient().RollbackTran();
                        _logger.LogDebug($"Commit Error , Rollback Transaction");
                    }
                    finally {
                        while (!TranStack.TryPop(out _)) {
                            Thread.Sleep(1);
                        }

                        _tranCount = TranStack.Count;
                    }
                }
            }
        }

        public void RollbackTran() {
            lock (this) {
                _tranCount--;
                GetDbClient().RollbackTran();
            }
        }

        public void RollbackTran(MethodInfo method) {
            lock (this) {
                string result = "";
                while (!TranStack.IsEmpty && !TranStack.TryPeek(out result)) {
                    Thread.Sleep(1);
                }

                if (result == method.GetFullName()) {
                    GetDbClient().RollbackTran();
                    _logger.LogDebug($"Rollback Transaction");
                    Console.WriteLine($"Rollback Transaction");
                    while (!TranStack.TryPop(out _)) {
                        Thread.Sleep(1);
                    }

                    _tranCount = TranStack.Count;
                }
            }
        }
    }
}